

//represents a single cell (expand comments for detailed explanations)
public class cell {
	
	//get the system newline character
	public static final String NL = System.getProperty("line.separator");
	
	/* hold the possible values in a bitmask:
	 * 	for possibility n (1 through 9) bitmask = 2^(n-1)
	 * 		1 : 2^0 = 000000001 = 1
	 * 		2 : 2^1 = 000000010 = 2
	 * 		3 : 2^2 = 000000100 = 4
	 * 		4 : 2^3 = 000001000 = 8
	 * 		5 : 2^4 = 000010000 = 16
	 * 		6 : 2^5 = 000100000 = 32
	 * 		7 : 2^6 = 001000000 = 64
	 * 		8 : 2^7 = 010000000 = 128
	 * 		9 : 2^8 = 100000000 = 256
	 * 
	 *	a cell with all values possible is the sum of these values: 511
	 *	a cell with only 1 possible value is an even power of 2
	 *  a cell with multiple possible values is the sum of those values (i.e. 1 and 3 = 1+4 = 5)
	 * 
	 */
    private int _bitmask = 511;
    
    /* a cell should know where it belongs in the puzzle.  The board is reponsible for setting the ID as cells are created
     * 
     */
    private int _id, _row, _col, _blk;

    public void setID(int i) {
    	_id = i;
    	//set row based on ID
    	for (int j = 0; j < 9; j++) {
    		if ( 9*j <= _id && _id <= (9*j)+8 ) { _row = j; }
    	}
    	//set column based on ID & row
    	for (int r = 0; r < 9; r++) {
    		for (int c = 0; c < 9; c++) {
    			if (_id == c+(9*r)) { _col = c; }
    		}
    	}
    	//set block based on row and column
    	for (int b = 0; b < 3; b++) {		
    		if (3*b <= _col && _col < (3*b)+3 && 0 <= _row && _row < 3 ) { _blk = b; }
    		if (3*b <= _col && _col < (3*b)+3 && 3 <= _row && _row < 6 ) { _blk = b + 3; }
    		if (3*b <= _col && _col < (3*b)+3 && 6 <= _row && _row < 9 ) { _blk = b + 6; }
    	}

    }
    
    public int getID()  {return _id; }
    public int getRow() { return _row; }
    public int getCol() { return _col; }
    public int getBlk() { return _blk; }
    
    //default constructor has bitmask value of 511, cell value of 0
    public cell() { }
    
    //parameterized constructor will set the value
    public cell(int i) { if ( 0 < i && i <= 9) { setValue(i); } }
    
    //show the bitmask value
    public int  getBitmask() { return _bitmask; }
    //set a known bitmask value (if between 1 and 511, else 511)
    public void setBitmask(int i) { _bitmask = (1 <= i && i <= 511) ? i : 511; }
    //reset to default
    public void reset() { _bitmask = 511; }
   
    /* toggle a bit using bitwise exclusive OR (results in 1 if both bits are different)
     * bits are numbered 1-9, representing the possible cell values
     *   000000101 (original value)
     *   000010000 (bit to toggle, #5 or 2^(5-1) or 2^4)
     *   ---------
     *   000010101 (result)
     */
    public void toggleBit(int i) { _bitmask ^= (int)Math.pow(2, i-1); }
    
    /* Set a bit to 1 using bitwise inclusive OR (results in 1 if either bit is 1)
     * bits are numbered 1-9, representing the possible cell values
     *   000000101 (original value)
     *   000010000 (bit to toggle, #5 or 2^(5-1) or 2^4)
     *   ---------
     *   000010101 (result)
     */
    public void setBit(int i) { _bitmask |= (int)Math.pow(2, i-1); }
    
    /* Clear a bit to 0 using bitwise NOT (invert all bits and then AND the results together (1 if both bits are 1)
     * bits are numbered 1-9, representing the possible cell values
     *   000000101 (original value)
     *   111101111 (bit to clear, #5 or 2^(5-1) or 2^4)... inverted before AND operation
     *   ---------
     *   000000101 (result)
     */
    public void clearBit(int i) { _bitmask &= ~(int)Math.pow(2, i-1); }
   
    /* To check if a bit is set, AND the bitmask with the desired value.  if the result equals the desired value, then true
     * bits are numbered 1-9, representing the possible cell values
     *   000010101 (original value)
     *   000010000 (bit to check, #5 or 2^(5-1) or 2^4)
     *   ---------
     *   000010000 (result equals the check value, so this is true)
     */
    public boolean isBitSet(int i) {
        return ((_bitmask & (int)Math.pow(2, i-1)) == (int)Math.pow(2, i-1) ) ? true : false;
    }
    
    /* The integer value (1-9) of the cell if known.  This will resolve correctly if only 1 bit is set in the bitmask.
     * find the exponent of base 2 from the cell's bitmask:
	 * 	example:  2^3=8 --> log2(8)=3
	 * 			
	 * 		for bitmask value y, find x (the exponent)
	 * 			  2^x=y --> log2(y)=x
	 * 
	 * 		using the rule:  the log of y with an exponent of x is x times the log of y
	 * 		log(y^x)=x*log(y)
	 * 		
	 * 		solving for x:  
	 * 		x = (log(y^x)) / log(y)
	 * 
	 * 		for bitmask value 8 (which is equivalent to 2^3)
	 * 		3 = (log(8)) / log(2)
	 * 
	 * 		this means that we can treat a bitmask value of 8 as a cell value of 3 (only the 3rd bit is set)
	 * 		when the solution is a whole number, we know we have an exponent of 2 in the bitmask, and therefore
	 * 		a single bit set in the bitmask.
	 * 
	 * 		if this value is not a whole number, then we do not have a whole number exponent of 2, meaning we
	 * 		have more than 1 bit set in the bitmask.
	 */
    public int getValue() {
    	double i = Math.log(_bitmask) / Math.log(2);  // find the exponent of 2 for the bitmask value
    	return (i == Math.round(i)) ? (int)i + 1 : 0; // if it's a whole number, return it (+1), otherwise return 0 (unknown)
    }
    
    /* Set the value of the bitmask according to a single bit (i):  2^(i-1).
     *   i.e. setValue(4) sets the bitmask to 2^(4-1) or 2^(3) or 8
     */
    public void setValue(int i) { _bitmask = (int)Math.pow(2, i-1); }
    
    /* return a 9-character string representing either the bitmask or known value of the cell:
     *  if value is unknown (0): returns "123456789" where each character is replaced by a "." if that bit is not set.
     * 	if value is known (>0):  returns all spaces, except the 5th character (middle) with shows the value.
     * 		used in the board class to display an ascii representation of the board.
     */
    public String toString() {
        String t = "";
        if (getValue() > 0) {
        	for (int i = 1; i < 10; i++) { t += (i==5) ? getValue() : " "; }
        }
        else {
        	for (int i = 1; i < 10; i++) { t += (isBitSet(i)) ? i : "."; }
        }
        return t;
    }
    
    // return a multi-line string representing the single cell for demo purposes only
    public String toStringCell() {
    	String t = toString();
    	String s = " ----- " + NL;
    	s += ": " + t.substring(0, 3) + " :" + " bitmask: " + getBitmask() + NL;
    	s += ": " + t.substring(3, 6) + " :" + " value:   " + getValue() + NL;
    	s += ": " + t.substring(6, 9) + " :" + NL;
    	s += " ----- " + NL;
    	
    	return s;
    }
}
